"""
自动备份核心模块

此模块包含自动备份功能的核心实现，负责备份任务的调度、执行和状态管理。
"""

import os
import shutil
import time
from datetime import datetime
from typing import Dict, Any, Optional, Tuple
from logger_module import log_info, log_warning, log_error

# PyQt5相关导入
from PyQt5.QtCore import QTimer, QObject, pyqtSignal

# 导入配置和模型
from .config import BackupConfig
from .models import BackupSettings, BackupRecord
from .utils import (
    validate_backup_path,
    generate_backup_filename,
    ensure_directory_exists,
    cleanup_old_backups,
    get_next_backup_time,
    parse_time_string,
    is_valid_frequency
)


class AutoBackupManager(QObject):
    """
    自动备份管理器
    
    负责管理数据库自动备份的所有核心功能，包括：
    - 设置的加载和保存
    - 备份任务的调度
    - 备份操作的执行
    - 状态监控和通知
    
    Signals:
        backup_started: 备份开始信号
        backup_completed: 备份完成信号，参数为(成功标志, 消息, 备份路径)
        backup_failed: 备份失败信号，参数为错误消息
    """
    
    # 定义信号
    backup_started = pyqtSignal()
    backup_completed = pyqtSignal(bool, str, str)
    backup_failed = pyqtSignal(str)
    
    def __init__(self, db_utils=None):
        """
        初始化自动备份管理器
        
        Args:
            db_utils: 数据库工具类实例，用于获取数据库路径和连接（可选）
        """
        super().__init__()
        
        # 初始化状态
        self._backup_timer = None
        self.db_utils = None
        self.config = None
        self.settings = BackupSettings()
        
        # 尝试初始化数据库工具
        try:
            # 如果没有提供db_utils，则自动导入
            if db_utils is None:
                try:
                    # 尝试不同的导入路径
                    try:
                        from db_utils import DatabaseUtils as db_utils
                    except ImportError:
                        # 尝试其他可能的导入路径
                        from utils.db_utils import DatabaseUtils as db_utils
                    except ImportError:
                        # 尝试直接导入模块
                        import db_utils
                except ImportError as ie:
                    log_warning(f"无法导入db_utils模块: {str(ie)}")
                    log_warning("自动备份功能可能无法正常工作")
            
            # 设置数据库工具实例
            self.db_utils = db_utils
            
            # 尝试初始化配置管理器
            try:
                self.config = BackupConfig(db_utils)
            except Exception as config_error:
                log_warning(f"无法初始化备份配置管理器: {str(config_error)}")
                log_warning("自动备份设置可能无法保存")
            
            # 初始化定时器
            self._init_timer()
            
            # 尝试加载设置
            try:
                self.load_settings()
            except Exception as load_error:
                log_warning(f"无法加载备份设置: {str(load_error)}")
                log_warning("将使用默认备份设置")
                
            log_info("自动备份管理器初始化完成")
            
        except Exception as e:
            log_error(f"自动备份管理器初始化失败: {str(e)}")
            # 即使初始化失败，也尽量保持部分功能可用
            self._init_timer()  # 至少初始化定时器
    
    def _init_timer(self):
        """初始化备份定时器
        确保定时器正确创建并连接信号
        """
        try:
            # 如果定时器已存在，先断开连接并删除
            if self._backup_timer is not None:
                try:
                    self._backup_timer.timeout.disconnect()
                except:
                    pass
                self._backup_timer.stop()
                self._backup_timer.deleteLater()
            
            # 创建新的定时器
            self._backup_timer = QTimer(self)
            # 设置单次触发为False，确保定时器持续运行
            self._backup_timer.setSingleShot(False)
            # 连接超时信号到检查函数
            self._backup_timer.timeout.connect(self._check_and_perform_backup)
            
            log_info("备份定时器初始化成功")
            
        except Exception as e:
            log_error(f"初始化备份定时器失败: {str(e)}")
            self._backup_timer = None
    
    def load_settings(self) -> Dict[str, Any]:
        """
        加载自动备份设置
        
        Returns:
            Dict: 备份设置字典，包含enabled, frequency, backup_time, backup_path
        """
        try:
            # 从配置管理器加载设置
            settings_dict = self.config.load_settings()
            
            # 更新内部设置对象
            self.settings.enabled = settings_dict.get('enabled', False)
            self.settings.frequency = settings_dict.get('frequency', '每天')
            self.settings.backup_time = settings_dict.get('backup_time', '00:00')
            self.settings.backup_path = settings_dict.get('backup_path', '')
            
            return settings_dict
            
        except Exception as e:
            log_error(f"加载自动备份设置失败: {str(e)}")
            # 返回默认设置
            return self.settings.to_dict()
    
    def update_settings(self, settings: Dict[str, Any]) -> bool:
        """
        更新备份设置
        
        Args:
            settings: 包含设置项的字典
                - enabled: bool, 是否启用自动备份
                - frequency: str, 备份频率('每天', '每周', '每月')
                - backup_time: str, 备份时间(HH:MM格式)
                - backup_path: str, 备份保存路径
        
        Returns:
            bool: 是否更新成功
        """
        try:
            # 创建临时设置对象进行验证
            temp_settings = BackupSettings(
                enabled=self.settings.enabled,
                frequency=self.settings.frequency,
                backup_time=self.settings.backup_time,
                backup_path=self.settings.backup_path
            )
            
            # 更新临时设置
            if 'enabled' in settings:
                temp_settings.enabled = settings['enabled']
            if 'frequency' in settings:
                temp_settings.frequency = settings['frequency']
            if 'backup_time' in settings:
                temp_settings.backup_time = settings['backup_time']
            if 'backup_path' in settings:
                temp_settings.backup_path = settings['backup_path']
            
            # 验证设置
            if hasattr(temp_settings, 'is_valid') and not temp_settings.is_valid():
                error_msg = temp_settings.get_validation_error_message() if hasattr(temp_settings, 'get_validation_error_message') else "设置验证失败"
                log_warning(f"备份设置验证失败: {error_msg}")
                return False
            
            # 如果启用了备份，还需要额外验证备份路径
            if temp_settings.enabled:
                # 确保备份目录存在
                if not ensure_directory_exists(temp_settings.backup_path):
                    log_error(f"无法创建备份目录: {temp_settings.backup_path}")
                    return False
                
                # 验证备份路径的写入权限
                if not validate_backup_path(temp_settings.backup_path):
                    log_error(f"备份路径无效或无写入权限: {temp_settings.backup_path}")
                    return False
            
            # 验证通过后，更新实际设置
            self.settings.enabled = temp_settings.enabled
            self.settings.frequency = temp_settings.frequency
            self.settings.backup_time = temp_settings.backup_time
            self.settings.backup_path = temp_settings.backup_path
            
            # 保存设置到数据库
            if self.config:
                save_success = self.config.save_settings(self.settings.to_dict())
                if not save_success:
                    log_error("保存备份设置到数据库失败")
                    return False
            
            # 更新调度器
            self._update_scheduler()
            
            log_info(f"备份设置已更新: 启用={self.settings.enabled}, "
                  f"频率={self.settings.frequency}, "
                  f"时间={self.settings.backup_time}, "
                  f"路径={self.settings.backup_path}")
            return True
            
        except Exception as e:
            log_error(f"更新备份设置失败: {str(e)}")
            # 记录详细异常信息以帮助调试
            import traceback
            traceback.print_exc()
            return False
    
    def start_scheduler(self):
        """
        启动备份调度器
        无论设置是否启用，都尝试启动调度器检查机制
        """
        # 确保定时器已初始化
        if not self._backup_timer:
            self._init_timer()
        
        # 启动定时器，每分钟检查一次
        self._backup_timer.start(60000)  # 60000毫秒 = 1分钟
        log_info("自动备份调度器已启动")
    
    def stop_scheduler(self):
        """
        停止备份调度器
        """
        if self._backup_timer and self._backup_timer.isActive():
            self._backup_timer.stop()
            log_info("自动备份调度器已停止")
    
    def perform_backup(self) -> Tuple[bool, str, str]:
        """
        立即执行数据库备份
        
        Returns:
            Tuple[bool, str, str]: (成功标志, 消息, 备份路径)
        """
        # 发送备份开始信号
        self.backup_started.emit()
        start_time = time.time()
        
        try:
            # 检查备份路径
            backup_path = self.settings.backup_path
            if not backup_path:
                raise ValueError("未设置备份路径")
            
            # 确保备份目录存在
            if not ensure_directory_exists(backup_path):
                raise ValueError(f"无法创建或访问备份目录: {backup_path}")
            
            # 验证备份路径的有效性
            if not validate_backup_path(backup_path):
                raise ValueError(f"备份路径无效或无写入权限: {backup_path}")
            
            # 获取数据库路径（尝试不同的方法名，兼容不同版本的db_utils）
            db_path = None
            try:
                # 尝试使用get_database_path方法
                if hasattr(self.db_utils, 'get_database_path'):
                    db_path = self.db_utils.get_database_path()
                # 如果没有该方法，尝试使用get_db_path方法
                elif hasattr(self.db_utils, 'get_db_path'):
                    db_path = self.db_utils.get_db_path()
                else:
                    raise AttributeError("数据库工具对象缺少获取数据库路径的方法")
            except Exception as db_error:
                raise ValueError(f"获取数据库路径失败: {str(db_error)}")
            
            if not db_path or not os.path.exists(db_path):
                raise ValueError(f"数据库文件不存在: {db_path}")
            
            # 生成备份文件名
            backup_filename = generate_backup_filename(db_path, 'auto_backup')
            full_backup_path = os.path.join(backup_path, backup_filename)
            
            # 执行备份（复制数据库文件），添加文件锁定处理
            try:
                # 尝试多次备份，防止文件锁定问题
                max_retries = 3
                retry_count = 0
                backup_success = False
                
                while retry_count < max_retries and not backup_success:
                    try:
                        # 对于SQLite数据库，使用shutil.copy2应该足够安全
                        # 但为了防止锁定问题，添加异常捕获和重试机制
                        shutil.copy2(db_path, full_backup_path)
                        backup_success = True
                    except PermissionError:
                        retry_count += 1
                        if retry_count < max_retries:
                            log_warning(f"数据库文件可能被锁定，2秒后重试 ({retry_count}/{max_retries})")
                            time.sleep(2)  # 等待2秒后重试
                        else:
                            raise
                
                # 验证备份文件是否创建成功且大小合理
                if not os.path.exists(full_backup_path):
                    raise Exception("备份文件未创建成功")
                
                backup_size = os.path.getsize(full_backup_path)
                original_size = os.path.getsize(db_path)
                
                # 验证备份文件大小是否合理（至少为原始文件的95%）
                if backup_size < original_size * 0.95:
                    raise Exception(f"备份文件不完整，大小异常")
                
            except Exception as copy_error:
                # 如果备份失败，清理可能的部分备份文件
                if os.path.exists(full_backup_path):
                    try:
                        os.remove(full_backup_path)
                    except:
                        pass
                raise copy_error
            
            # 清理旧备份，保留最新的10个
            try:
                deleted_count = cleanup_old_backups(backup_path, 10)
                if deleted_count > 0:
                    log_info(f"已清理 {deleted_count} 个旧备份文件")
            except Exception as cleanup_error:
                log_error(f"清理旧备份时出错: {str(cleanup_error)}")
                # 不阻止备份成功，仅记录错误
            
            # 计算备份持续时间
            duration = time.time() - start_time
            
            # 创建详细的备份记录
            try:
                record = BackupRecord(
                    backup_path=full_backup_path,
                    success=True,
                    file_size=backup_size,
                    duration=duration
                )
                log_info(f"备份详情 - 大小: {record.get_file_size_str() if hasattr(record, 'get_file_size_str') else f'{backup_size} bytes'}, 耗时: {duration:.2f}秒")
            except Exception as record_error:
                log_error(f"创建备份记录时出错: {str(record_error)}")
            
            success_msg = f"数据库备份成功: {full_backup_path}"
            log_info(success_msg)  # 在实际应用中应该使用日志系统
            
            # 发送备份完成信号
            self.backup_completed.emit(True, success_msg, full_backup_path)
            
            return True, success_msg, full_backup_path
            
        except Exception as e:
            error_msg = f"执行备份失败: {str(e)}"
            log_error(error_msg)  # 在实际应用中应该使用日志系统
            
            # 创建失败记录
            try:
                record = BackupRecord(
                    backup_path="",
                    success=False,
                    error_message=error_msg
                )
            except Exception as record_error:
                log_error(f"创建失败备份记录时出错: {str(record_error)}")
            
            # 发送备份失败信号
            self.backup_failed.emit(error_msg)
            
            return False, error_msg, ""
    
    def _update_scheduler(self):
        """
        根据当前设置更新备份调度器
        """
        if self.settings.enabled:
            # 启用状态下，确保调度器运行
            if not self._backup_timer or not self._backup_timer.isActive():
                self.start_scheduler()
        else:
            # 禁用状态下，停止调度器
            self.stop_scheduler()
    
    def _check_and_perform_backup(self):
        """
        检查当前时间是否需要执行备份，如果需要则执行
        增加更精确的时间比较和日志记录
        """
        # 如果自动备份未启用，直接返回
        if not self.settings.enabled:
            log_info("自动备份未启用，跳过备份检查")
            return
        
        # 获取当前时间
        now = datetime.now()
        current_time = now.strftime('%H:%M')
        current_weekday = now.weekday()  # 0-6, 0是星期一
        current_day = now.day
        
        # 获取配置的备份时间和频率
        backup_time = self.settings.backup_time
        frequency = self.settings.frequency
        
        # 详细日志记录
        log_info("备份检查 - 当前时间: {current_time}, 频率: {frequency}, 配置时间: {backup_time}")
        
        # 检查是否需要执行备份
        should_backup = False
        reason = ""
        
        # 允许时间有1分钟的容错范围，避免因精确到秒的问题错过备份
        # 首先解析时间
        try:
            backup_hour, backup_minute = map(int, backup_time.split(':'))
            current_hour, current_minute = map(int, current_time.split(':'))
            
            # 检查时间是否匹配（允许当前分钟等于备份分钟）
            time_matches = (current_hour == backup_hour and current_minute == backup_minute)
            
            if time_matches:
                if frequency == '每天':
                    should_backup = True
                    reason = "每日备份时间匹配"
                elif frequency == '每周' and current_weekday == 0:  # 每周一
                    should_backup = True
                    reason = "每周一备份时间匹配"
                elif frequency == '每月' and current_day == 1:  # 每月1号
                    should_backup = True
                    reason = "每月1号备份时间匹配"
        except ValueError:
            log_error(f"备份时间格式错误: {backup_time}")
            return
        
        # 如果需要备份，则执行
        if should_backup:
            log_info(f"触发备份: {reason}")
            # 执行备份并记录结果
            success, message, path = self.perform_backup()
            log_info(f"备份执行结果: {success}, 消息: {message}")
        else:
            log_info(f"未触发备份 - 当前时间: {current_time}, 频率: {frequency}, 配置时间: {backup_time}")
    
    def get_current_status(self) -> Dict[str, Any]:
        """
        获取当前备份状态
        
        Returns:
            Dict: 包含当前状态信息的字典
        """
        return {
            'enabled': self.settings.enabled,
            'frequency': self.settings.frequency,
            'backup_time': self.settings.backup_time,
            'backup_path': self.settings.backup_path,
            'scheduler_running': self._backup_timer.isActive() if self._backup_timer else False
        }
    
    def validate_settings(self) -> Tuple[bool, str]:
        """
        验证当前设置的有效性
        
        Returns:
            Tuple[bool, str]: (是否有效, 错误消息或空字符串)
        """
        # 如果未启用，设置始终有效
        if not self.settings.enabled:
            return True, ""
        
        # 检查备份路径
        if not self.settings.backup_path:
            return False, "未设置备份路径"
        
        # 验证路径
        if not validate_backup_path(self.settings.backup_path):
            return False, f"无效的备份路径: {self.settings.backup_path}"
        
        # 检查频率设置
        valid_frequencies = ['每天', '每周', '每月']
        if self.settings.frequency not in valid_frequencies:
            return False, f"无效的备份频率: {self.settings.frequency}"
        
        # 检查时间格式
        try:
            hour, minute = map(int, self.settings.backup_time.split(':'))
            if not (0 <= hour <= 23 and 0 <= minute <= 59):
                return False, "时间格式无效，应在00:00-23:59范围内"
        except:
            return False, "时间格式无效，应为HH:MM格式"
        
        return True, ""
    
    def start_backup_service(self, settings: Dict[str, Any]):
        """
        启动备份服务
        
        Args:
            settings: 备份设置字典，包含enabled, frequency, backup_time, backup_path
        """
        try:
            # 更新设置
            self.settings.enabled = settings.get('enabled', False)
            self.settings.frequency = settings.get('frequency', '每天')
            self.settings.backup_time = settings.get('backup_time', '00:00')
            self.settings.backup_path = settings.get('backup_path', '')
            
            # 更新调度器
            self._update_scheduler()
            log_info("备份服务已启动")
        except Exception as e:
            log_error(f"启动备份服务失败: {str(e)}")
    
    def stop_backup_service(self):
        """
        停止备份服务
        """
        try:
            # 更新设置为禁用
            self.settings.enabled = False
            
            # 停止调度器
            self.stop_scheduler()
            log_info("备份服务已停止")
        except Exception as e:
            log_error(f"停止备份服务失败: {str(e)}")
    
    def save_settings(self, settings):
        """
        保存备份设置（兼容BackupSettings对象和字典）
        
        Args:
            settings: BackupSettings对象或字典
            
        Returns:
            bool: 是否保存成功
        """
        try:
            # 处理输入参数，支持对象或字典
            if hasattr(settings, 'to_dict'):
                # 如果是BackupSettings对象，调用to_dict方法
                settings_dict = settings.to_dict()
            else:
                # 否则视为字典
                settings_dict = settings
            
            # 调用更新设置方法
            return self.update_settings(settings_dict)
        except Exception as e:
            log_error(f"保存备份设置失败: {str(e)}")
            return False